import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import javax.swing.ImageIcon;
import jmetest.renderer.TestSkybox;
import jmetest.terrain.TestTerrain;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.InputActionInterface;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.CameraNode;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.FormatConverter;
import com.jmex.model.converters.ObjToJme;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.material.Material;
import com.jmex.physics.util.SimplePhysicsGame;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.HillHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;

public class HummerTest extends SimplePhysicsGame{

    // Манай машин
    protected Car jeep;
    private InputAction resetAction;	
    private int cars=4;
   
    CameraNode camNode=null;
    private Spatial spatial;
    private Skybox m_skybox;     
	
	@Override
	protected void simpleInitGame() {
		display.setTitle("My little hummers ;)");
		//Тэнгэрийг үүсгэж байнаа
		createSkyBox();
		
        // Уул толгодыг үүсгэх гэж байна даа
		final StaticPhysicsNode staticNode = getPhysicsSpace().createStaticNode();
        Spatial terrain = createTerrain();
        staticNode.attachChild( terrain );
        staticNode.setMaterial(Material.GRANITE);
        staticNode.getLocalTranslation().set( 0, -150, 0 );
        rootNode.attachChild( staticNode );
        staticNode.generatePhysicsGeometry();
        
        
		// ODEJava физикээ тохируулах
        tunePhysics();        
        // Машинаа үүсгэх
        createCar();
        // Машинаа жолоодох тохиргоог хийх
        initInput();
        
        createObj();
        lightState.setEnabled(false);        
	}
	
	@Override
	protected void simpleUpdate() {
		super.simpleUpdate();
		Vector3f temp = new Vector3f(cam.getLocation());
	    m_skybox.setLocalTranslation(temp);	    
	}
	
	// Манай үндсэн метод. Бүгд эндээс эхлэнэ
	public static void main(String[] args) {
		new HummerTest().start();
	}
	
	private void createSkyBox(){
		// skybox үүсгэх
		m_skybox = new Skybox("skybox", 20, 20, 20);

        Texture north = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/north.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture south = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/south.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture east = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/east.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture west = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/west.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture up = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/top.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture down = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/bottom.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);

        m_skybox.setTexture(Skybox.Face.North, north);
        m_skybox.setTexture(Skybox.Face.West, west);
        m_skybox.setTexture(Skybox.Face.South, south);
        m_skybox.setTexture(Skybox.Face.East, east);
        m_skybox.setTexture(Skybox.Face.Up, up);
        m_skybox.setTexture(Skybox.Face.Down, down);
        m_skybox.preloadTextures();
        
	    rootNode.attachChild(m_skybox);
	}
	
	private void createObj(){
		URL folder= HummerTest.class.getClassLoader().getResource("data/hummer_jeep/");
		URL model = HummerTest.class.getClassLoader().getResource("data/hummer_jeep/hummer_jeep.obj");
        FormatConverter converter=new ObjToJme();
        converter.setProperty("mtllib", folder);
        converter.setProperty("texdir", folder);
        ByteArrayOutputStream BO=new ByteArrayOutputStream();
        try {
            converter.convert(model.openStream(), BO);
            spatial=(Spatial) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            spatial.setLocalScale(4f); 
            spatial.setModelBound(new BoundingBox());
            spatial.updateModelBound();            
            DynamicPhysicsNode jeep = getPhysicsSpace().createDynamicNode();            
            jeep.attachChild(spatial);
            jeep.setLocalScale(0.8f);          
            jeep.setMaterial(Material.IRON);
            jeep.generatePhysicsGeometry();   
            jeep.setLocalTranslation(new Vector3f(0,50,0));
            rootNode.attachChild(jeep);            
        } catch (Exception e) {
            System.out.println("Damn exceptions! O_o \n" + e);
            e.printStackTrace();
            System.exit(0);
        }
	}
	
    private void tunePhysics() {
        getPhysicsSpace().setAutoRestThreshold( 0.2f );
        // Бусад тохиолдолд физик удаан байх болно
        setPhysicsSpeed( 3 );    
    }
    
    /**
     * Машинаа үүсгээд скэнэ графлуу нэмнэ
     */
    private void createCar() {
        jeep = new Car(getPhysicsSpace());        
        rootNode.attachChild( jeep );
        jeep.setLocalTranslation(new Vector3f(0,10,0));
        
        for(int i=0;i<cars;i++){
        	Car mashin = new Car(getPhysicsSpace());
        	mashin.attachChild(mashin);
        	mashin.getLocalTranslation().setY(i*30+30);
        	mashin.getLocalTranslation().setX(i*30+30);
        	mashin.getLocalTranslation().setZ(i*30+30);
        	mashin.setName("mashin"+i);
        	rootNode.attachChild(mashin);
        }  
         
    }
    
    /**
     * Камерыг манай машиныг дагахаар тохируулаад зарим оролтын үйлдлүүдийг
     * тохируулна
     */
    private void initInput() {
        
        Camera camer = cam;
        //camer.setFrustum(1.0f, 1000.0f, -0.55f, 0.55f, 0.4125f, -0.4125f);
        camer.setFrustumFar(1000f);
        camer.update();
        
        camNode = new CameraNode("Camera Node", camer);
        Vector3f tempLoc = new Vector3f(jeep.getChassis().getLocalTranslation());
        tempLoc.y+=2f;
        tempLoc.x-=3f;
        camNode.setLocalTranslation(tempLoc);
        Quaternion tempRot = new Quaternion(jeep.getChassis().getLocalRotation());
        tempRot.fromAngleAxis(-FastMath.PI/2, Vector3f.UNIT_Y);
        camNode.setLocalRotation(tempRot);      
        camNode.updateWorldData(0);
        jeep.getChassis().attachChild(camNode);        

        
        // Өөрийн үйлдлүүдийг оролт баригчдад бүртгүүлж байна.
        input.addAction( new AccelAction( jeep, 1 ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_U, InputHandler.AXIS_NONE, false );
        input.addAction( new AccelAction( jeep, -1 ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_J, InputHandler.AXIS_NONE, false );
        input.addAction( new SteerAction( jeep, -1 ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_H, InputHandler.AXIS_NONE, false );
        input.addAction( new SteerAction( jeep, 1 ),
                InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_K, InputHandler.AXIS_NONE, false );

        resetAction = new ResetAction();
        input.addAction( resetAction, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_R, InputHandler.AXIS_NONE, false );        
    }
    
	private Spatial createTerrain() {
		// Гэрэл
		DirectionalLight dl = new DirectionalLight();
		dl.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
		dl.setDirection(new Vector3f(1, -0.5f, 1));
		dl.setEnabled(true);
		lightState.attach(dl);

		// террайн
		HillHeightMap heightMap = new HillHeightMap(129, 2000, 5.0f, 20.0f,
				(byte) 2);
		heightMap.setHeightScale(0.001f);
		Vector3f terrainScale = new Vector3f(10, 1, 10);
		TerrainPage terrain = new TerrainPage("Terrain", 33, heightMap
				.getSize(), terrainScale, heightMap.getHeightMap());
		terrain.setDetailTexture(1, 16);
		//rootNode.attachChild(terrain);

		// Текстур зурагнууд
		ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
		        heightMap);
		    pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
		                                .getResource("jmetest/data/texture/grassb.png")),
		                  -128, 0, 128);
		    pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
		                                .getResource("jmetest/data/texture/dirt.jpg")),
		                  0, 128, 255);
		    pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
		                                .getResource("jmetest/data/texture/highest.jpg")),
		                  128, 255,
		                  384);

		    pt.createTexture(256);

		TextureState ts = display.getRenderer().createTextureState();
		ts.setEnabled(true);
		Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
				Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
		ts.setTexture(t1, 0);

		Texture t2 = TextureManager.loadTexture(TestTerrain.class
				.getClassLoader().getResource("jmetest/data/texture/Detail.jpg"),
				Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear);
		ts.setTexture(t2, 1);
		t2.setWrap(Texture.WrapMode.Repeat);

		t1.setApply(Texture.ApplyMode.Combine);
		t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate);
		t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
		t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
		t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor);
		t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

		t2.setApply(Texture.ApplyMode.Combine);
		t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned);
		t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
		t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
		t2.setCombineSrc1RGB(Texture.CombinerSource.Previous);
		t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);
		rootNode.setRenderState(ts);
		terrain.setLocalScale(new Vector3f(1f,0.7f,1f));
		return terrain;     
        
    }
    
    /**
     * Дугуйг жолоодон залах энгийн оролтын үйлдэлийг гүйцэтгэдэг класс
     */
    private class SteerAction implements InputActionInterface {
        Car car;
        int direction;
        public SteerAction( Car car, int direction ) {
            this.car = car;
            this.direction = direction;
        }

        public void performAction( final InputActionEvent e ) {
            // Хэрэв товч дарагдаастай байвал 
            if ( e.getTriggerPressed() ) {
                car.steer( direction );
            }
            // Дараад авбал 
            else {
                car.unsteer();
            }
        }

    }

    /**
     * Машиныг хурдасгах үүрэгтэй класс
     */
    private class AccelAction implements InputActionInterface {
        Car car;
        int direction;
        public AccelAction( final Car car, final int direction ) {
            this.car = car;
            this.direction = direction;
        }

        public void performAction( final InputActionEvent e ) {
            // Хэрэв товч дарагдсан бол
            if ( e.getTriggerPressed() ) {
                car.accelerate( direction );
            }
            // Товч авагдсан бол
            else {
                car.releaseAccel();
            }
        }
    }

    //Машиныг дахин байрлуулах
    private class ResetAction extends InputAction {
        public void performAction( InputActionEvent evt ) {
        	jeep.setPosition( 0, 10, 0 );
        }
    }    

}
